﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;

namespace GMap.NET
{
    // 回调函数
    public delegate void MoveTo(GPoint pt);

    public class Kinetic
    {
        // 阀值
        // In most cases changing the threshold isn't needed. In px/ms, default to 0.
        double threshold = 0;
        // 减速
        // {Float} the deseleration in px/ms², default to 0.0035.
        double deceleration = 0.0015;
        // 采集点最大数量
        // {Integer} the number of points we use to calculate the kinetic, initial values.
        int nbPoints = 100;
        // 延迟
        // {Float} time to consider to calculate the kinetic initial values. In ms, default to 200.
        double delay = 200;
        // 采集到的鼠标点
        // List of points use to calculate the kinetic initial values.
        List<MousePoint> points = new List<MousePoint>();
        // 动画的时间间隔10毫秒
        // {Integer} Interval in milliseconds between 2 steps in the "kinetic 
        // scrolling". Applies only if enableKinetic is set. Defaults
        // to 10 milliseconds.
        const int kineticInterval = 10;
        // 动画计时器
        // ID of the timer.
        Timer timerId = new Timer();

        // 开始拖动
        // Begins the dragging.
        public void begin()
        {
            // Animation.stop(this.timerId)     // 停止上一次的动画
            timerId.Stop();
            points.Clear();         // 采集点初始化
        }
        // 鼠标正在移动
        // Updates during the dragging.
        public void update(double x, double y)
        {
            points.Insert(0, new MousePoint(x, y));
            if (points.Count > nbPoints) points.RemoveAt(points.Count - 1);
        }
        // 结束移动 返回最终速度 和 运动方向 (方向是9点钟为0, 12点钟为π/2, 3点钟为π, 6点钟为3π/2)
        // {Object} An object with two properties: 
        // "speed", and "theta". The "speed" and "theta" values are to be passed to the move 
        // function when starting the animation.
        public MouseInfo end(double x, double y)
        {
            long now = (DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000;
            MousePoint last = null;
            MousePoint point = null;
            for (int i = 0; i < points.Count; i++) {
                point = points[i];
                if (now - point.time > delay) {
                    break;
                }
                last = point;
            }
            if (last == null) return null;

            // 求速度 像素/毫秒
            long time = now - last.time;
            double dist = Math.Sqrt(Math.Pow(x - last.x, 2) +
                                    Math.Pow(y - last.y, 2));
            double speed = dist / time;
            if (speed == 0 || speed < threshold) return null;

            // 求方向 希腊字母θ
            double theta = Math.Asin((y - last.y) / dist);
            if (last.x <= x) {
                theta = Math.PI - theta;
            }
            return new MouseInfo(speed, theta);
        }
        // 继续移动地图
        // Launch the kinetic move pan.
        // info - {Object} An object with two properties, "speed", and "theta".
        // These values are those returned from the "end" call.
        // callback - {Function} Function called on every step of the animation,
        // receives x, y (values to pan), end (is the last point).
        public void move(MouseInfo info, MoveTo moveTo)
        {
            double v0 = info.speed;
            double fx = Math.Cos(info.theta);
            double fy = -Math.Sin(info.theta);
            long initialTime = (DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000;     // 开始时间

            double lastX = 0;
            double lastY = 0;
            int count = 0;

            timerId.Stop();
            timerId.Dispose();
            timerId = new Timer();
            timerId.Interval = kineticInterval;
            //timerId = new Timer(kineticInterval);
            timerId.Tick += (object sender, EventArgs e) => {
                // 滑动的动画
                long t = (DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000 - initialTime;
                double p = (-this.deceleration * Math.Pow(t, 2)) / 2.0 + v0 * t;
                double x = p * fx;
                double y = p * fy;
                bool end = false;


                double v = -this.deceleration * t + v0;         // 速度 = 初速度v0 - 加速度 * 时间
                if (v <= 0) {
                    timerId.Stop();
                    end = true;
                }

                double arg_x = x - lastX;
                double arg_y = y - lastY;
                lastX = x;
                lastY = y;
                if (moveTo != null) moveTo(new GPoint((long)-arg_x, (long)-arg_y));
            };
            
            timerId.Start();
        }


    }
    /// <summary>
    /// 鼠标经过点
    /// </summary>
    public class MousePoint
    {
        public double x = 0;       // 鼠标点X(像素)
        public double y = 0;       // 鼠标点Y(像素)
        public long time = 0;       // 当前时间(毫秒)

        public MousePoint(double x, double y) {
            this.x = x;
            this.y = y;
            this.time = (DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000;
        }
    }
    /// <summary>
    /// 鼠标最终状态
    /// </summary>
    public class MouseInfo
    {
        public double speed;
        public double theta;
        public MouseInfo(double speed, double theta) {
            this.speed = speed;
            this.theta = theta;
        }
    }
}
